//
// NetworkInterface.cpp
//
// $Id: //poco/1.4/Net/src/NetworkInterface.cpp#9 $
//
// Library: Net
// Package: NetCore
// Module:  NetworkInterface
//
// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#include "Poco/Net/NetworkInterface.h"


#ifdef POCO_NET_HAS_INTERFACE


#include "Poco/Net/DatagramSocket.h"
#include "Poco/Net/NetException.h"
#include "Poco/Format.h"
#include "Poco/RefCountedObject.h"
#include "Poco/Format.h"
#include <cstring>
#include <iostream>
#include <iomanip>

using Poco::format;
using Poco::FastMutex;
using Poco::format;


std::ostream& operator<<(std::ostream& os, const Poco::Net::NetworkInterface::MACAddress& mac)
{
	std::ios state(0);
	state.copyfmt(os);
	for (unsigned i = 0; i < mac.size(); ++i)
	{
		if (i > 0) os << Poco::Net::NetworkInterface::MAC_SEPARATOR;
		os << std::hex << std::setw(2) << std::setfill('0') << (unsigned) mac[i];
	}
	os.copyfmt(state);
	return os;
}


namespace Poco {
namespace Net {


//
// NetworkInterfaceImpl
//

class NetworkInterfaceImpl: public Poco::RefCountedObject
{
public:
	typedef NetworkInterface::AddressTuple AddressTuple;
	typedef NetworkInterface::AddressList  AddressList;
	typedef NetworkInterface::Type         Type;

	NetworkInterfaceImpl(unsigned index);
	NetworkInterfaceImpl(const std::string& name, const std::string& displayName, const std::string& adapterName, const IPAddress& address, unsigned index, NetworkInterface::MACAddress* pMACAddress = 0);
	NetworkInterfaceImpl(const std::string& name, const std::string& displayName, const std::string& adapterName, unsigned index = 0, NetworkInterface::MACAddress* pMACAddress = 0);
	NetworkInterfaceImpl(const std::string& name,
		const std::string& displayName,
		const std::string& adapterName,
		const IPAddress& address,
		const IPAddress& subnetMask,
		const IPAddress& broadcastAddress,
		unsigned index,
		NetworkInterface::MACAddress* pMACAddress = 0);

	unsigned index() const;
	const std::string& name() const;
	const std::string& displayName() const;
	const std::string& adapterName() const;
	const IPAddress& firstAddress(IPAddress::Family family) const;
	void addAddress(const AddressTuple& address);
	const IPAddress& address(unsigned index) const;
	const NetworkInterface::AddressList& addressList() const;
	bool hasAddress(const IPAddress& address) const;
	const IPAddress& subnetMask(unsigned index) const;
	const IPAddress& broadcastAddress(unsigned index) const;
	const IPAddress& destAddress(unsigned index) const;
	const NetworkInterface::MACAddress& macAddress() const;
	bool supportsIPv4() const;
	bool supportsIPv6() const;

	void setName(const std::string& name);
	void setDisplayName(const std::string& name);
	void setAdapterName(const std::string& name);
	void addAddress(const IPAddress& addr);
	void setMACAddress(const NetworkInterface::MACAddress& addr);
	void setMACAddress(const void *addr, std::size_t len);

	unsigned mtu() const;
	unsigned ifindex() const;
	Type type() const;

	bool broadcast() const;
	bool loopback() const;
	bool multicast() const;
	bool pointToPoint() const;
	bool running() const;
	bool up() const;

	void setFlags(short flags);

	void setUp(bool up);
	void setMTU(unsigned mtu);
	void setType(Type type);
	void setIndex(unsigned index);
	void setPhyParams();

protected:
	~NetworkInterfaceImpl();

private:
	std::string m_name;
	std::string m_displayName;
	std::string m_adapterName;
	AddressList m_addressList;
	unsigned    m_index;
	bool        m_broadcast;
	bool        m_loopback;
	bool        m_multicast;
	bool        m_pointToPoint;
	bool        m_up;
	bool        m_running;
	unsigned    m_mtu;
	Type        m_type;

	NetworkInterface::MACAddress m_macAddress;

	friend class NetworkInterface;
};


NetworkInterfaceImpl::NetworkInterfaceImpl(unsigned index):
	m_index(index),
	m_broadcast(false),
	m_loopback(false),
	m_multicast(false),
	m_pointToPoint(false),
	m_up(false),
	m_running(false),
	m_mtu(0),
	m_type(NetworkInterface::NI_TYPE_OTHER)
{
}


NetworkInterfaceImpl::NetworkInterfaceImpl(const std::string& name, const std::string& displayName, const std::string& adapterName, const IPAddress& address, unsigned index, NetworkInterface::MACAddress* pMACAddress):
	m_name(name),
	m_displayName(displayName),
	m_adapterName(adapterName),
	m_index(index),
	m_broadcast(false),
	m_loopback(false),
	m_multicast(false),
	m_pointToPoint(false),
	m_up(false),
	m_running(false),
	m_mtu(0),
	m_type(NetworkInterface::NI_TYPE_OTHER)
{
	m_addressList.push_back(AddressTuple(address, IPAddress(), IPAddress()));
	setPhyParams();
	if (pMACAddress) setMACAddress(*pMACAddress);
}


NetworkInterfaceImpl::NetworkInterfaceImpl(const std::string& name, const std::string& displayName, const std::string& adapterName, unsigned index, NetworkInterface::MACAddress* pMACAddress):
	m_name(name),
	m_displayName(displayName),
	m_adapterName(adapterName),
	m_index(index),
	m_broadcast(false),
	m_loopback(false),
	m_multicast(false),
	m_pointToPoint(false),
	m_up(false),
	m_running(false),
	m_mtu(0),
	m_type(NetworkInterface::NI_TYPE_OTHER)
{
	setPhyParams();
	if (pMACAddress) setMACAddress(*pMACAddress);
}


NetworkInterfaceImpl::NetworkInterfaceImpl(const std::string& name,
	const std::string& displayName,
	const std::string& adapterName,
	const IPAddress& address,
	const IPAddress& subnetMask,
	const IPAddress& broadcastAddress,
	unsigned index,
	NetworkInterface::MACAddress* pMACAddress):
	m_name(name),
	m_displayName(displayName),
	m_adapterName(adapterName),
	m_index(index),
	m_broadcast(false),
	m_loopback(false),
	m_multicast(false),
	m_pointToPoint(false),
	m_up(false),
	m_running(false),
	m_mtu(0)
{
	m_addressList.push_back(AddressTuple(address, subnetMask, broadcastAddress));
	setPhyParams();
	if (pMACAddress) setMACAddress(*pMACAddress);
}


void NetworkInterfaceImpl::setPhyParams()
{
#if defined(POCO_OS_FAMILY_UNIX)
	struct ifreq ifr;
	std::strncpy(ifr.ifr_name, m_name.c_str(), IFNAMSIZ);
	DatagramSocket ds;

	ds.ioctl(SIOCGIFFLAGS, &ifr);
	setFlags(ifr.ifr_flags);

	ds.ioctl(SIOCGIFMTU, &ifr);
	setMTU(ifr.ifr_mtu);
#endif
}


NetworkInterfaceImpl::~NetworkInterfaceImpl()
{
}


bool NetworkInterfaceImpl::supportsIPv4() const
{
	AddressList::const_iterator it = m_addressList.begin();
	AddressList::const_iterator end = m_addressList.end();
	for (; it != end; ++it)
	{
		if (IPAddress::IPv4 == std::get<NetworkInterface::IP_ADDRESS>(*it).family())
			return true;
	}

	return false;
}


bool NetworkInterfaceImpl::supportsIPv6() const
{
#ifdef POCO_HAVE_IPv6
	AddressList::const_iterator it = m_addressList.begin();
	AddressList::const_iterator end = m_addressList.end();
	for (; it != end; ++it)
	{
		if (IPAddress::IPv6 == std::get<NetworkInterface::IP_ADDRESS>(*it).family())
			return true;
	}
#endif
	return false;
}


inline unsigned NetworkInterfaceImpl::index() const
{
	return m_index;
}


inline const std::string& NetworkInterfaceImpl::name() const
{
	return m_name;
}


inline const std::string& NetworkInterfaceImpl::displayName() const
{
	return m_displayName;
}


inline const std::string& NetworkInterfaceImpl::adapterName() const
{
	return m_adapterName;
}


const IPAddress& NetworkInterfaceImpl::firstAddress(IPAddress::Family family) const
{
	AddressList::const_iterator it = m_addressList.begin();
	AddressList::const_iterator end = m_addressList.end();
	for (;it != end; ++it)
	{
		const IPAddress& addr = std::get<NetworkInterface::IP_ADDRESS>(*it);
		if (addr.family() == family) return addr;
	}

	throw NotFoundException(format("%s family address not found.", (family == IPAddress::IPv4) ? "IPv4" : "IPv6"));
}


inline void NetworkInterfaceImpl::addAddress(const AddressTuple& address)
{
	m_addressList.push_back(address);
}


bool NetworkInterfaceImpl::hasAddress(const IPAddress& address) const
{
	NetworkInterface::ConstAddressIterator it = m_addressList.begin();
	NetworkInterface::ConstAddressIterator end = m_addressList.end();
	for (; it != end; ++it)
	{
		if (std::get<NetworkInterface::IP_ADDRESS>(*it) == address)
			return true;
	}
	return false;
}


inline const IPAddress& NetworkInterfaceImpl::address(unsigned index) const
{
	if (index < m_addressList.size()) return std::get<NetworkInterface::IP_ADDRESS>(m_addressList[index]);
	else throw NotFoundException(format("No address with index %u.", index));
}


inline const NetworkInterface::AddressList& NetworkInterfaceImpl::addressList() const
{
	return m_addressList;
}


const IPAddress& NetworkInterfaceImpl::subnetMask(unsigned index) const
{
	if (index < m_addressList.size())
		return std::get<NetworkInterface::SUBNET_MASK>(m_addressList[index]);

	throw NotFoundException(format("No subnet mask with index %u.", index));
}


const IPAddress& NetworkInterfaceImpl::broadcastAddress(unsigned index) const
{
	if (index < m_addressList.size())
		return std::get<NetworkInterface::BROADCAST_ADDRESS>(m_addressList[index]);

	throw NotFoundException(format("No subnet mask with index %u.", index));
}


const IPAddress& NetworkInterfaceImpl::destAddress(unsigned index) const
{
	if (!pointToPoint())
		throw InvalidAccessException("Only PPP addresses have destination address.");
	else if (index < m_addressList.size())
		return std::get<NetworkInterface::BROADCAST_ADDRESS>(m_addressList[index]);

	throw NotFoundException(format("No address with index %u.", index));
}


const NetworkInterface::MACAddress& NetworkInterfaceImpl::macAddress() const
{
	return m_macAddress;
}


inline unsigned NetworkInterfaceImpl::mtu() const
{
	return m_mtu;
}


inline NetworkInterface::Type NetworkInterfaceImpl::type() const
{
	return m_type;
}


inline bool NetworkInterfaceImpl::broadcast() const
{
	return m_broadcast;
}


inline bool NetworkInterfaceImpl::loopback() const
{
	return m_loopback;
}


inline bool NetworkInterfaceImpl::multicast() const
{
	return m_multicast;
}


inline bool NetworkInterfaceImpl::pointToPoint() const
{
	return m_pointToPoint;
}


inline bool NetworkInterfaceImpl::running() const
{
	return m_running;
}


inline bool NetworkInterfaceImpl::up() const
{
	return m_up;
}


void NetworkInterfaceImpl::setFlags(short flags)
{
#ifdef POCO_OS_FAMILY_UNIX
	m_broadcast = ((flags & IFF_BROADCAST) != 0);
	m_loopback = ((flags & IFF_LOOPBACK) != 0);
	m_multicast = ((flags & IFF_MULTICAST) != 0);
	m_pointToPoint = ((flags & IFF_POINTOPOINT) != 0);
	m_running = ((flags & IFF_RUNNING) != 0);
	m_up = ((flags & IFF_UP) != 0);
#endif
}


inline void NetworkInterfaceImpl::setUp(bool up)
{
	m_up = up;
}


inline void NetworkInterfaceImpl::setMTU(unsigned mtu)
{
	m_mtu = mtu;
}


inline void NetworkInterfaceImpl::setType(Type type)
{
	m_type = type;
}


inline void NetworkInterfaceImpl::setIndex(unsigned index)
{
	m_index = index;
}


inline void NetworkInterfaceImpl::setName(const std::string& name)
{
	m_name = name;
}


inline void NetworkInterfaceImpl::setDisplayName(const std::string& name)
{
	m_displayName = name;
}


inline void NetworkInterfaceImpl::setAdapterName(const std::string& name)
{
	m_adapterName = name;
}


inline void NetworkInterfaceImpl::addAddress(const IPAddress& addr)
{
	m_addressList.push_back(AddressTuple(addr, IPAddress(), IPAddress()));
}


inline void NetworkInterfaceImpl::setMACAddress(const NetworkInterface::MACAddress& addr)
{
	m_macAddress = addr;
}


inline void NetworkInterfaceImpl::setMACAddress(const void *addr, std::size_t len)
{
	m_macAddress.clear();
	m_macAddress.insert(m_macAddress.end(), static_cast<const unsigned char*>(addr), static_cast<const unsigned char*>(addr) + len);
}


//
// NetworkInterface
//


FastMutex NetworkInterface::m_mutex;


NetworkInterface::NetworkInterface(unsigned index):
	m_pImpl(new NetworkInterfaceImpl(index))
{
}


NetworkInterface::NetworkInterface(const NetworkInterface& interfc):
	m_pImpl(interfc.m_pImpl)
{
	m_pImpl->duplicate();
}


NetworkInterface::NetworkInterface(const std::string& name, const std::string& displayName, const std::string& adapterName, const IPAddress& address, unsigned index, MACAddress* pMACAddress):
	m_pImpl(new NetworkInterfaceImpl(name, displayName, adapterName, address, index, pMACAddress))
{
}


NetworkInterface::NetworkInterface(const std::string& name, const std::string& displayName, const std::string& adapterName, unsigned index, MACAddress* pMACAddress):
	m_pImpl(new NetworkInterfaceImpl(name, displayName, adapterName, index, pMACAddress))
{
}


NetworkInterface::NetworkInterface(const std::string& name, const IPAddress& address, unsigned index, MACAddress* pMACAddress):
	m_pImpl(new NetworkInterfaceImpl(name, name, name, address, index, pMACAddress))
{
}


NetworkInterface::NetworkInterface(const std::string& name,
	const std::string& displayName,
	const std::string& adapterName,
	const IPAddress& address,
	const IPAddress& subnetMask,
	const IPAddress& broadcastAddress,
	unsigned index,
	MACAddress* pMACAddress):
	m_pImpl(new NetworkInterfaceImpl(name, displayName, adapterName, address, subnetMask, broadcastAddress, index, pMACAddress))
{
}


NetworkInterface::NetworkInterface(const std::string& name,
	const IPAddress& address,
	const IPAddress& subnetMask,
	const IPAddress& broadcastAddress,
	unsigned index,
	MACAddress* pMACAddress):
	m_pImpl(new NetworkInterfaceImpl(name, name, name, address, subnetMask, broadcastAddress, index, pMACAddress))
{
}


NetworkInterface::~NetworkInterface()
{
	m_pImpl->release();
}


NetworkInterface& NetworkInterface::operator = (const NetworkInterface& interfc)
{
	NetworkInterface tmp(interfc);
	swap(tmp);
	return *this;
}


void NetworkInterface::swap(NetworkInterface& other)
{
	using std::swap;
	swap(m_pImpl, other.m_pImpl);
}


unsigned NetworkInterface::index() const
{
	return m_pImpl->index();
}


const std::string& NetworkInterface::name() const
{
	return m_pImpl->name();
}


const std::string& NetworkInterface::displayName() const
{
	return m_pImpl->displayName();
}


const std::string& NetworkInterface::adapterName() const
{
	return m_pImpl->adapterName();
}


const IPAddress& NetworkInterface::firstAddress(IPAddress::Family family) const
{
	return m_pImpl->firstAddress(family);
}


void NetworkInterface::firstAddress(IPAddress& addr, IPAddress::Family family) const
{
	try
	{
		addr = firstAddress(family);
	}
	catch (NotFoundException&)
	{
		addr = IPAddress(family);
	}
}


void NetworkInterface::addAddress(const IPAddress& address)
{
	m_pImpl->addAddress(AddressTuple(address, IPAddress(), IPAddress()));
}


void NetworkInterface::addAddress(const IPAddress& address, const IPAddress& subnetMask, const IPAddress& broadcastAddress)
{
	m_pImpl->addAddress(AddressTuple(address, subnetMask, broadcastAddress));
}


const IPAddress& NetworkInterface::address(unsigned index) const
{
	return m_pImpl->address(index);
}


const NetworkInterface::AddressList& NetworkInterface::addressList() const
{
	return m_pImpl->addressList();
}


const IPAddress& NetworkInterface::subnetMask(unsigned index) const
{
	return m_pImpl->subnetMask(index);
}


const IPAddress& NetworkInterface::broadcastAddress(unsigned index) const
{
	return m_pImpl->broadcastAddress(index);
}


const NetworkInterface::MACAddress& NetworkInterface::macAddress() const
{
	return m_pImpl->macAddress();
}


const IPAddress& NetworkInterface::destAddress(unsigned index) const
{
	return m_pImpl->destAddress(index);
}


unsigned NetworkInterface::mtu() const
{
	return m_pImpl->mtu();
}


NetworkInterface::Type NetworkInterface::type() const
{
	return m_pImpl->type();
}


bool NetworkInterface::supportsIP() const
{
	return m_pImpl->supportsIPv4() || m_pImpl->supportsIPv6();
}


bool NetworkInterface::supportsIPv4() const
{
	return m_pImpl->supportsIPv4();
}


bool NetworkInterface::supportsIPv6() const
{
	return m_pImpl->supportsIPv6();
}


bool NetworkInterface::supportsBroadcast() const
{
	return m_pImpl->broadcast();
}


bool NetworkInterface::supportsMulticast() const
{
	return m_pImpl->multicast();
}


bool NetworkInterface::isLoopback() const
{
	return m_pImpl->loopback();
}


bool NetworkInterface::isPointToPoint() const
{
	return m_pImpl->pointToPoint();
}


bool NetworkInterface::isRunning() const
{
	return m_pImpl->running();
}


bool NetworkInterface::isUp() const
{
	return m_pImpl->up();
}


NetworkInterface NetworkInterface::forName(const std::string& name, bool requireIPv6)
{
	Map map = NetworkInterface::map(false, false);
	Map::const_iterator it = map.begin();
	Map::const_iterator end = map.end();

	for (; it != end; ++it)
	{
		if (it->second.name() == name && ((requireIPv6 && it->second.supportsIPv6()) || !requireIPv6))
			return it->second;
	}
	throw InterfaceNotFoundException(name);
}


NetworkInterface NetworkInterface::forName(const std::string& name, IPVersion ipVersion)
{
	Map map = NetworkInterface::map(false, false);
	Map::const_iterator it = map.begin();
	Map::const_iterator end = map.end();

	for (; it != end; ++it)
	{
		if (it->second.name() == name)
		{
			if (ipVersion == IPv4_ONLY && it->second.supportsIPv4())
				return it->second;
			else if (ipVersion == IPv6_ONLY && it->second.supportsIPv6())
				return it->second;
			else if (ipVersion == IPv4_OR_IPv6)
				return it->second;
		}
	}
	throw InterfaceNotFoundException(name);
}


NetworkInterface NetworkInterface::forAddress(const IPAddress& addr)
{
	Map map = NetworkInterface::map(true, false);
	Map::const_iterator it = map.begin();
	Map::const_iterator end = map.end();

	for (; it != end; ++it)
	{
		const std::size_t count = it->second.addressList().size();
		for (int i = 0; i < count; ++i)
		{
			if (it->second.address(i) == addr)
				return it->second;
		}
	}
	throw InterfaceNotFoundException(addr.toString());
}


NetworkInterface NetworkInterface::forIndex(unsigned i)
{
	if (i != NetworkInterface::NO_INDEX)
	{
		Map map = NetworkInterface::map(false, false);
		Map::const_iterator it = map.find(i);
		if (it != map.end())
			return it->second;
		else
			throw InterfaceNotFoundException("#" + format("%i",i));
	}
	throw InterfaceNotFoundException("#" + format("%i",i));
}


NetworkInterface::List NetworkInterface::list(bool ipOnly, bool upOnly)
{
	List list;
	Map m = map(ipOnly, upOnly);
	NetworkInterface::Map::const_iterator it = m.begin();
	NetworkInterface::Map::const_iterator end = m.end();
	for (; it != end; ++it)
	{
		int index = it->second.index();
		std::string name = it->second.name();
		std::string displayName = it->second.displayName();
		std::string adapterName = it->second.adapterName();
		NetworkInterface::MACAddress mac = it->second.macAddress();

		typedef NetworkInterface::AddressList List;
		const List& ipList = it->second.addressList();
		List::const_iterator ipIt = ipList.begin();
		List::const_iterator ipEnd = ipList.end();
		for (int counter = 0; ipIt != ipEnd; ++ipIt, ++counter)
		{
			IPAddress addr = std::get<NetworkInterface::IP_ADDRESS>(*ipIt);
			IPAddress mask = std::get<NetworkInterface::SUBNET_MASK>(*ipIt);
			NetworkInterface ni;
			if (mask.isWildcard())
			{
				ni = NetworkInterface(name, displayName, adapterName, addr, index, &mac);
			}
			else
			{
				IPAddress broadcast = std::get<NetworkInterface::BROADCAST_ADDRESS>(*ipIt);
				ni = NetworkInterface(name, displayName, adapterName, addr, mask, broadcast, index, &mac);
			}

			ni.m_pImpl->m_broadcast    = it->second.m_pImpl->m_broadcast;
			ni.m_pImpl->m_loopback     = it->second.m_pImpl->m_loopback;
			ni.m_pImpl->m_multicast    = it->second.m_pImpl->m_multicast;
			ni.m_pImpl->m_pointToPoint = it->second.m_pImpl->m_pointToPoint;
			ni.m_pImpl->m_up           = it->second.m_pImpl->m_up;
			ni.m_pImpl->m_running      = it->second.m_pImpl->m_running;
			ni.m_pImpl->m_mtu          = it->second.m_pImpl->m_mtu;
			ni.m_pImpl->m_type         = it->second.m_pImpl->m_type;

			list.push_back(ni);
		}
	}

	return list;
}


} } // namespace Poco::Net


#if POCO_OS == POCO_OS_LINUX
//
// Linux
//


#include <sys/types.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <linux/if_packet.h>
#include <net/if_arp.h>
#include <iostream>

namespace Poco {
namespace Net {


namespace {

static NetworkInterface::Type fromNative(unsigned arphrd)
{
	switch (arphrd)
	{
	case ARPHRD_ETHER:     return NetworkInterface::NI_TYPE_ETHERNET_CSMACD;
	case ARPHRD_IEEE802:   return NetworkInterface::NI_TYPE_ISO88025_TOKENRING;
	case ARPHRD_DLCI:      return NetworkInterface::NI_TYPE_FRAMERELAY;
	case ARPHRD_PPP:       return NetworkInterface::NI_TYPE_PPP;
	case ARPHRD_LOOPBACK:  return NetworkInterface::NI_TYPE_SOFTWARE_LOOPBACK;
	case ARPHRD_ATM:       return NetworkInterface::NI_TYPE_ATM;
	case ARPHRD_IEEE80211: return NetworkInterface::NI_TYPE_IEEE80211;
	case ARPHRD_TUNNEL:
	case ARPHRD_TUNNEL6:   return NetworkInterface::NI_TYPE_TUNNEL;
	case ARPHRD_IEEE1394:  return NetworkInterface::NI_TYPE_IEEE1394;
	default:               return NetworkInterface::NI_TYPE_OTHER;
	}
}

void setInterfaceParams(struct ifaddrs* iface, NetworkInterfaceImpl& impl)
{
	struct sockaddr_ll* sdl = (struct sockaddr_ll*) iface->ifa_addr;
	impl.setName(iface->ifa_name);
	impl.setDisplayName(iface->ifa_name);
	impl.setAdapterName(iface->ifa_name);
	impl.setPhyParams();

	impl.setMACAddress(sdl->sll_addr, sdl->sll_halen);
	impl.setType(fromNative(sdl->sll_hatype));
}

}


NetworkInterface::Map NetworkInterface::map(bool ipOnly, bool upOnly)
{
	FastMutex::ScopedLock lock(m_mutex);
	Map result;
	unsigned ifIndex = 0;
	NetworkInterface intf;
	Map::iterator ifIt;

	struct ifaddrs* ifaces = 0;
	struct ifaddrs* iface = 0;

	if (getifaddrs(&ifaces) < 0)
		throw NetException("cannot get network adapter list");

	try
	{
		for (iface = ifaces; iface; iface = iface->ifa_next)
		{
			if (!iface->ifa_addr) continue;

			IPAddress address, subnetMask, broadcastAddress;
			unsigned family = iface->ifa_addr->sa_family;
			switch (family)
			{
			case AF_PACKET:
			{
				struct sockaddr_ll* sll = (struct sockaddr_ll*)iface->ifa_addr;
				ifIndex = sll->sll_ifindex;
				intf = NetworkInterface(ifIndex);
				setInterfaceParams(iface, intf.impl());

				if ((result.find(ifIndex) == result.end()) && ((upOnly && intf.isUp()) || !upOnly))
					ifIt = result.insert(Map::value_type(ifIndex, intf)).first;

				break;
			}
			case AF_INET:
				ifIndex = if_nametoindex(iface->ifa_name);
				ifIt = result.find(ifIndex);
				intf = NetworkInterface(ifIndex);
				setInterfaceParams(iface, intf.impl());

				if ((ifIt == result.end()) && ((upOnly && intf.isUp()) || !upOnly))
					ifIt = result.insert(Map::value_type(ifIndex, intf)).first;

				address = IPAddress(*(iface->ifa_addr));
				subnetMask = IPAddress(*(iface->ifa_netmask));

				if (iface->ifa_flags & IFF_BROADCAST && iface->ifa_broadaddr)
					broadcastAddress = IPAddress(*(iface->ifa_broadaddr));
				else if (iface->ifa_flags & IFF_POINTOPOINT && iface->ifa_dstaddr)
					broadcastAddress = IPAddress(*(iface->ifa_dstaddr));
				else
					broadcastAddress = IPAddress();

				break;
#if defined(POCO_HAVE_IPv6)
			case AF_INET6:
				ifIndex = if_nametoindex(iface->ifa_name);
				ifIt = result.find(ifIndex);
				intf = NetworkInterface(ifIndex);
				setInterfaceParams(iface, intf.impl());

				if ((ifIt == result.end()) && ((upOnly && intf.isUp()) || !upOnly))
					result.insert(Map::value_type(ifIndex, intf));

				address = IPAddress(&reinterpret_cast<const struct sockaddr_in6*>(iface->ifa_addr)->sin6_addr, sizeof(struct in6_addr), ifIndex);
				subnetMask = IPAddress(*(iface->ifa_netmask));
				broadcastAddress = IPAddress();

				break;
#endif
			default:
				continue;
			}

			if (family == AF_INET
#ifdef POCO_HAVE_IPv6
			|| family == AF_INET6
#endif
			)
			{
				intf = NetworkInterface(std::string(iface->ifa_name), address, subnetMask, broadcastAddress, ifIndex);
				if ((upOnly && intf.isUp()) || !upOnly)
				{
					if ((ifIt = result.find(ifIndex)) != result.end())
						ifIt->second.addAddress(address, subnetMask, broadcastAddress);
				}
			}
		} // for interface
	}
	catch (...)
	{
		if (ifaces) freeifaddrs(ifaces);
		throw;
	}

	if (ifaces) freeifaddrs(ifaces);

	if (ipOnly)
	{
		Map::iterator it = result.begin();
		Map::iterator end = result.end();
		for (; it != end;)
		{
			if (!it->second.supportsIPv4() && !it->second.supportsIPv6())
				result.erase(it++);
			else ++it;
		}
	}

	return result;
}


} } // namespace Poco::Net

#endif // POCO_OS_LINUX


#endif // POCO_NET_HAS_INTERFACE
